/*Copyright*/
package com.jmex.physics;

import com.jme.math.Vector3f;

/**
 * An axis for a {@link Joint}. An axis can belong to only one Joint. Each axis adds one degree of freedom to a joint.
 *
 * @author Irrisor
 * @see Joint
 * @see Joint#createRotationalAxis()
 * @see Joint#createTranslationalAxis()
 */
public abstract class JointAxis {

    JointAxis() {
    }

    private Joint joint;

    public Joint getJoint() {
        return joint;
    }

    void setJoint( Joint joint ) {
        this.joint = joint;
    }

    /**
     * @return true if this axis imposes translational freedom in the axis direction.
     */
    public abstract boolean isTranslationalAxis();

    /**
     * @return true if this axis imposes rotational freedom around the axis direction.
     */
    public abstract boolean isRotationalAxis();

    /**
     * store direction of this axis.
     */
    private final Vector3f direction = new Vector3f();

    /**
     * Sets the direction of this axis.
     *
     * @param direction new direction in world coordinate space if the joint is attached to the world, object coordinate
     *                  space if attached to two objects
     * @see #isRelativeToSecondObject()
     */
    public void setDirection( Vector3f direction ) {
        this.direction.set( direction ).normalizeLocal();
    }

    /**
     * Query the direction of this axis. The
     * passed in Vector3f will be populated with the values, and then returned.
     *
     * @param store where to store the direction (null to create a new vector)
     * @return store
     */
    public Vector3f getDirection( Vector3f store ) {
        if ( store == null ) {
            store = new Vector3f();
        }
        store.set( direction );
        return store;
    }

    /**
     * Query position of the objects attached to this joint. For a translational axis this is in direction of this axis,
     * for a rotational axis this is around the axis direction in radians.
     *
     * @return the relative position of the objects attached to the joint
     */
    public abstract float getPosition();

    /**
     * @return the relative velocity (translation/angular speed) of the objects attached to the joint in/around
     *         direction of this axis
     */
    public abstract float getVelocity();


    /**
     * Change the force the joint motor can use to reach the desired velocity.
     *
     * @param value new acceleration than can be used
     * @see #setDesiredVelocity(float)
     */
    public abstract void setAvailableAcceleration( float value );

    /**
     * @return the maximum force available to reach the desired velocity
     * @see #setAvailableAcceleration
     */
    public abstract float getAvailableAcceleration();

    /**
     * Change the desired velocity. The joint motor uses the available linear acceleration to reach the specified
     * velocity as fast as possible.
     *
     * @param value new desired linear velocity
     * @see #setAvailableAcceleration(float)
     */
    public abstract void setDesiredVelocity( float value );

    /**
     * @return the last set desired velocity
     * @see #setDesiredVelocity(float)
     */
    public abstract float getDesiredVelocity();

    /**
     * Change the smallest allowed position.
     *
     * @param value smallest allowed position, {@link Float#NEGATIVE_INFINITY} to disable minimum
     */
    public abstract void setPositionMinimum( float value );

    /**
     * @return smallest allowed position, {@link Float#NEGATIVE_INFINITY} if minimum is disabled
     */
    public abstract float getPositionMinimum();

    /**
     * Change the largest allowed position.
     *
     * @param value largest allowed position, {@link Float#POSITIVE_INFINITY} to disable maximum
     */
    public abstract void setPositionMaximum( float value );

    /**
     * @return largest allowed position, {@link Float#POSITIVE_INFINITY} if maximum is disabled
     */
    public abstract float getPositionMaximum();

    /**
     * Copy axis data from another axis.
     *
     * @param toCopy where to read values from
     */
    public void copy( JointAxis toCopy ) {
        if ( isRotationalAxis() != toCopy.isRotationalAxis() ) {
            throw new IllegalArgumentException( "cannot copy from linear to rotational axis or vice versa" );
        }
        setDirection( toCopy.getDirection( null ) );
        setDesiredVelocity( toCopy.getDesiredVelocity() );
        setAvailableAcceleration( toCopy.getAvailableAcceleration() );
        setRelativeToSecondObject( toCopy.isRotationalAxis() );
        setPositionMaximum( toCopy.getPositionMaximum() );
        setPositionMinimum( toCopy.getPositionMinimum() );
    }


    /**
     * getter for field relativeToSecondObject
     *
     * @return true if this axis' direction is given in coordinates relative to the second object. The default is false
     *         which means it is relative to the first object (resp. the world)
     */
    public boolean isRelativeToSecondObject() {
        return this.relativeToSecondObject;
    }

    /**
     * store the value for field relativeToSecondObject
     */
    private boolean relativeToSecondObject;

    /**
     * setter for field relativeToSecondObject
     *
     * @param value new value
     */
    public void setRelativeToSecondObject( final boolean value ) {
        this.relativeToSecondObject = value;
    }
}

/*
 * $log$
 */

